package com.kws.commercial.util;

import com.kws.common.core.exception.ServiceException;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioAPI {

	/**
	 * minio参数
	 */
	private String ENDPOINT;
	private String ACCESS_KEY;
	private String SECRET_KEY;
	private String BUCKET;

	/**
	 * 桶占位符
	 */
	private static final String BUCKET_PARAM = "${bucket}";
	/**
	 * bucket权限-只读
	 */
	private static final String READ_ONLY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";
	/**
	 * bucket权限-只读
	 */
	private static final String WRITE_ONLY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";
	/**
	 * bucket权限-读写
	 */
	private static final String READ_WRITE = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";

	/**
	 * 获取minio连接
	 * @return
	 */
	@Bean
	public MinioClient getMinioClient(){
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();
		return client;
	}
	/**
	 * 文件url前半段
	 *
	 * @param bucket 桶
	 * @return 前半段
	 */
	public String getObjectPrefixUrl(String bucket) {
		return String.format("%s/%s/", ENDPOINT, bucket);
	}

	/**
	 * 创建桶
	 *
	 * @param bucket 桶
	 */
	public void makeBucket(String bucket) throws Exception {
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();
		// 判断桶是否存在
		boolean isExist = client.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
		if (!isExist) {
			// 新建桶
			client.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
		}
	}

	/**
	 * 更新桶权限策略
	 *
	 * @param bucket 桶
	 * @param policy 权限
	 */
	public void setBucketPolicy(String bucket, String policy) throws Exception {
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();
		switch (policy) {
			case "read-only":
				client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).config(READ_ONLY.replace(BUCKET_PARAM, bucket)).build());
				break;
			case "write-only":
				client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).config(WRITE_ONLY.replace(BUCKET_PARAM, bucket)).build());
				break;
			case "read-write":
				client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).config(READ_WRITE.replace(BUCKET_PARAM, bucket)).build());
				break;
			case "none":
			default:
				break;
		}
	}

	/**
	 * 上传本地文件
	 *
	 * @param bucket    桶
	 * @param objectKey 文件key
	 * @param filePath  文件路径
	 * @return 文件url
	 */
	public String uploadFile(String bucket, String objectKey, String filePath) throws Exception {
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();
		client.uploadObject(UploadObjectArgs.builder().bucket(bucket).object(objectKey).filename(filePath).contentType("image/png").build());
		return getObjectPrefixUrl(bucket) + objectKey;
	}

	/**
	 * 流式上传文件
	 *
	 * @param bucket      桶
	 * @param objectKey   文件key
	 * @param inputStream 文件输入流
	 * @param contentType 文件输入流类型
	 * @return 文件url
	 */
	public String uploadInputStream(String bucket, String objectKey, InputStream inputStream, String contentType) throws Exception {
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();
		client.putObject(PutObjectArgs.builder().bucket(bucket).object(objectKey).stream(inputStream, inputStream.available(), -1).contentType(contentType).build());
		return getObjectPrefixUrl(bucket) + objectKey;
	}

	/**
	 * 下载文件
	 *
	 * @param bucket    桶
	 * @param objectKey 文件key
	 * @return 文件流
	 */
	public InputStream download(String bucket, String objectKey) throws Exception {
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();
		return client.getObject(GetObjectArgs.builder().bucket(bucket).object(objectKey).build());
	}

	/**
	 * 分片下载
	 *
	 * @param bucket    桶
	 * @param objectKey 文件key
	 * @return 文件流
	 */
	public InputStream partDownload(String bucket, String objectKey,Long offset,Long length) throws Exception {
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();

		StatObjectResponse response = client.statObject(StatObjectArgs.builder().bucket(bucket).object(objectKey).build());
		if(offset > response.size()){
			throw new ServiceException("超出文件大小,请重新设置");
		}

		return client.getObject(GetObjectArgs.builder().bucket(bucket).object(objectKey).offset(offset).length(length).build());
	}

	/**
	 * 获取文件大小
	 *
	 * @param bucket    桶
	 * @param objectKey 文件key
	 * @return Long
	 */
	public Long getFileSize(String bucket, String objectKey) throws Exception {
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();
		StatObjectResponse response = client.statObject(StatObjectArgs.builder().bucket(bucket).object(objectKey).build());
		return response.size();
	}

	/**
	 * 文件复制
	 *
	 * @param sourceBucket    源桶
	 * @param sourceObjectKey 源文件key
	 * @param bucket          桶
	 * @param objectKey       文件key
	 * @return 新文件url
	 */
	public String copyFile(String sourceBucket, String sourceObjectKey, String bucket, String objectKey) throws Exception {
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();
		CopySource source = CopySource.builder().bucket(sourceBucket).object(sourceObjectKey).build();
		client.copyObject(CopyObjectArgs.builder().bucket(bucket).object(objectKey).source(source).build());
		return getObjectPrefixUrl(bucket) + objectKey;
	}

	/**
	 * 删除文件
	 *
	 * @param bucket    桶
	 * @param objectKey 文件key
	 */
	public void deleteFile(String bucket, String objectKey) throws Exception {
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();
		client.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectKey).build());
	}

	/**
	 * 获取文件签名url
	 *
	 * @param bucket    桶
	 * @param objectKey 文件key
	 * @param expires   签名有效时间  单位秒
	 * @return 文件签名地址
	 */
	public String getSignedUrl(String bucket, String objectKey, int expires) throws Exception {
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();
		return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucket).object(objectKey).expiry(expires).build());
	}

	/**
	 * 合并文件
	 * @param bucket       桶名称
	 * @param chunkPath    分片文件存放路径
	 * @param chunchName   分片文件名称
	 * @param fileNamePath 合并后文件存放路径
	 * @param fileName	    合并后文件名称
	 * @param chunkNum      分片数量
	 * @return 返回合并后文件存放路径
	 */
	public String compose(String bucket,String chunkPath,String chunchName,String fileNamePath,String fileName,Long chunkNum,Long totalSize) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
		MinioClient client = MinioClient.builder().endpoint(ENDPOINT).credentials(ACCESS_KEY, SECRET_KEY).build();
		List<ComposeSource> sources = new ArrayList<>();
		//不存在文件分片
		if(chunkNum==1){
			ComposeSource composeSource = ComposeSource.builder().bucket(bucket).object(chunkPath+"/"+chunchName).build();
			sources.add(composeSource);
		}else {
			for (int i = 1; i <= chunkNum; i++) {
				//指定分块文件的信息
				ComposeSource composeSource = ComposeSource.builder().bucket(bucket).object(chunkPath+"/" + i+chunchName).build();
				sources.add(composeSource);
			}
		}
		//指定合并后的objectName等信息
		ComposeObjectArgs composeObjectArgs = ComposeObjectArgs.builder()
			.bucket(bucket)
			.object(fileNamePath+"/"+fileName)
			.sources(sources)//指定源文件
			.build();
		//合并文件,
		//报错size 1048576 must be greater than 5242880，minio默认的分块文件大小为5M
		client.composeObject(composeObjectArgs);
		//TODO 此处还差文件合并后文件和原文件对比，然后删除分块文件

		return fileNamePath+"/"+fileName;
	}
}
